#ifndef CLASS_BUFFER
#define CLASS_BUFFER

/********************************************************************
 * Esta clase implementa un buffer. Este tiene los registros y bloques
 * mas recientemente usados.
 * Los archivos buscan los datos en este antes de ir a buscar a disco, 
 * y con esto, mejora el rendimiento.
 * Tambien permite controlar la memoria otorgada al intercambio de 
 * datos entre el disco y la aplicacion.
 *******************************************************************/ 

#include "archivo.h"
#include "observador.h"
#include "manejadorArchivos.h" 
#include "bloque.h"
#include <list>

class Buffer : public Observador {
	
	private:
		char * buf;
		unsigned int size;
		unsigned int espacio_libre;
		unsigned int cant_validos;
		/*
		 * Estructura que se almacena en los nodos de la lista
		 */ 
		struct tNodo{
			Archivo * archivo;
			unsigned int num_bloque;
			bool valido;
			bool modificado;
			char * posicion;
			unsigned int tam_ocupado;
		};
		typedef struct tNodo Nodo;
		//*******************************************************//
		
		std::list<Nodo *> lista_bloques;
						
		static Buffer * instance;
		
		Buffer(Buffer &b);
		
		Buffer(unsigned int tam);
		
		/*
		 * Libera, por lo menos, la cantidad de bytes pasada por 
		 * parametro
		 */ 
		void liberar_espacio(unsigned int bytes);
		
		/*
		 * Busca un bloque en la lista de los cargados. Si no existe o
		 * esta invalido, devuelve NULL
		 */ 
		Nodo * buscar_bloque(Archivo &arch, unsigned int num_bloque);
		
		bool alocar_bloque(Archivo &arch, Bloque * bloque, bool modif = false);
		
		void liberar_bloque_LRU();
		
		void compactar(Nodo* eliminado);
		
		void guardar_en_disco (Archivo* arch);
		
		std::string generar_encabezado();
	
	public:	
		
		static Buffer * get_instance(unsigned int tam = 0);
		
		~Buffer();
		
		/*
		 * Devuelve una copia del bloque pasado por parametro
		 * correspondiente al archivo.
		 * El tamano del bloque devuelto es igual al tamano del
		 * registro base del archivo.
		 *   
		 */
		Bloque * get_bloque(Archivo &archivo, unsigned int num_bloque) throw (ExcepLeerBloque);
		
		/*
		 * Copia en el buffer el bloque pasado por parametro.
		 * Este necesariamente debe existir, pues todos los datos estan
		 * cacheados.
		 * El tamano del bloque, debe coincidir con el tamanio que figura
		 * en el archivo.
		 */ 
		void guardar_bloque(Archivo &archivo, Bloque * bloque) throw (ExcepEscribirBloque);
		
		/*
		 * Busca el bloque correspondiente en el archivo y lo settea como
		 * no valido, por lo tanto no es guardado cuando le libera del buffer. 
		 */
		void invalidar_bloque(Archivo &arch, unsigned int num_bloque);
		 													
 		static void destroy();
 		
 		void actualizar(Observable * observable, void * param);

		void map_buffer();
 	
};
#endif

